///////////////////////////////////////////////////////////////////////////////
//
// Copyright
//
///////////////////////////////////////////////////////////////////////////////

package ru.goodcore.datastructure
{

import ru.goodcore.errors.GenericError;
import ru.goodcore.errors.GenericErrorCode;

/**
 * Implements double-linked list structute
 * 
 * 
 * @author					Dimarik
 * @version					1.0
 * @playerversion			Flash 9
 * @langversion				3.0
 * 
 * @see						http://en.wikipedia.org/wiki/Linked_list
 *
 * @keyword					doublelinkedlist, linkedlist, datastructure
 */
public class DoubleLinkedList implements IIterableAggregate
{

	//-------------------------------------------------------------------------
	//
	//  Constructor
	//
	//-------------------------------------------------------------------------

	/**
	 * Constructor
	 */
	public function DoubleLinkedList() {
		super();
	}

	//-------------------------------------------------------------------------
	//
	//  Private variables
	//
	//-------------------------------------------------------------------------

	/**
	 * @private 
	 */
	internal var $head:			DoubleLinkedListNode;

	/**
	 * @private 
	 */
	private var _tail:			DoubleLinkedListNode;

	/**
	 * @private 
	 */
	private var _length:	uint = 0;

	public function get length():uint {
		return this._length;
	}

	//-------------------------------------------------------------------------
	//
	//  Interface Implementation: IIteratableAggregate
	//
	//-------------------------------------------------------------------------

	public function createIterator(node:* = null):IIterator {
		return new Iterator(node || this.$head);
	}

	public function isEmpty():Boolean {
		return this._length == 0;
	}

	/**
	 * Adds one or more elements to the tail of the list and returns the new length of the list.
	 *  
	 * @param 	args
	 * @return	An integer representing the length of the new list
	 * 
	 */
	public function append(...args):uint {
		var node:	DoubleLinkedListNode;
		var tail:	DoubleLinkedListNode = this._tail;

		var i:		uint;
		var len:	uint = (args as Array).length;

		if ( !tail ) {
			node = new DoubleLinkedListNode();
			node.value = args[0];
			tail = this.$head = node;
			i = 1;
		}

		for (; i < len; i++) {
			node = new DoubleLinkedListNode();
			node.value = args[i];
			node.$prev = tail;
			tail.$next = node;
			tail = node;
		}

		this._length += len;

		this._tail = tail;

		return this._length;
	}

	/**
	 * 
	 * @return 
	 * 
	 */
	public function removeTail():* {
		var node:DoubleLinkedListNode = this._tail;

		if (
			   node
			&& (this._tail = node.prev)
		) {
			return node.value;
		}

		return null;
	}


	public function prepend(...args):DoubleLinkedListNode {
		return null;
	}

	public function removeHead():* {
		var node:DoubleLinkedListNode = this.$head;

		if (
			   node
			&& (this.$head = node.next)
		) {
			return node.value;
		}

		return null;
	}

	public function join(sep:*):String {
		return null;
	}

	public function reverse():void {
		
	}

/*
	public function nodeOf(obj:*, from:DListIterator = null):DoubleLinkedListNode {
		return null;
	}

	public function lastNodeOf(obj:*, from:DListIterator = null):DoubleLinkedListNode {
		return null;
	}
*/
	public function sort(...args):DoubleLinkedList {
		return null;
	}

	public function concat(...args):DoubleLinkedList {
		return null;
	}

	public function merge(...args):DoubleLinkedList {
		return null;
	}


	public function splice(iterator:IIterator, deleteCount:uint, ...args):DoubleLinkedList {
		var result:DoubleLinkedList = new DoubleLinkedList();

		if (iterator.hasNext) {
			var node:	DoubleLinkedListNode = iterator.next;
			
			var next:	DoubleLinkedListNode;
			var prev:	DoubleLinkedListNode = node.$prev;

			if (
				   deleteCount
				//&& deleteCount < 
			) {
				result.$head = node;
				node.$prev = null;

				var lastNode:	DoubleLinkedListNode = node;

				while (--deleteCount) {
					lastNode = lastNode.$next;
				}

				next = lastNode.$next;

				result._tail = lastNode;
				lastNode.$next = null;

				if ( !prev ) {
					prev = this.$head = next;
				} else {
					prev.$next = next;
				}
			}

			var len:	uint = (args as Array).length;

			if (len) {
				var i:uint = 0;

				if ( !prev ) {
					node = new DoubleLinkedListNode();
					node.value = args[i++];
					prev = node;
					next = this.$head;
				}

				if ( !next )
					next = prev.$next;

				for (; i < len; i++) {
					node = new DoubleLinkedListNode();
					node.value = args[i];
					prev.$next = node;
					prev = node;
				}

				node.$next = next;

			}

		}

		return result;
	}

	public function toString():String {
		return '';
	}


}
}

//=============================================================================
//
//   Inner definitions
//
//=============================================================================

import ru.goodcore.datastructure.DoubleLinkedList;
import ru.goodcore.datastructure.IIterator;
import flash.errors.IllegalOperationError;
import ru.goodcore.datastructure.DoubleLinkedListNode;
	
//-----------------------------------------------------------------------------
//
//   Iterator
//
//-----------------------------------------------------------------------------
/**
 * @private
 */
internal class Iterator implements IIterator {

	public function Iterator(node:DoubleLinkedListNode) {
		super();
		this._current = node;
	}

	/**
	 * 
	 */	
	private var _current:	DoubleLinkedListNode;

	//-------------------------------------------------------------------------
	//
	//   Interface implementation: IIterator
	//
	//-------------------------------------------------------------------------

	//---------------------------------
	//  hasNext
	//---------------------------------

	public function get hasNext():Boolean {
		return this._current != null;
	}

	//---------------------------------
	//  hasPrev
	//---------------------------------

	public function get hasPrev():Boolean {
		return this._current.prev != null;
	}

	//---------------------------------
	//  next
	//---------------------------------

	public function get next():* {
		var node:	DoubleLinkedListNode = this._current;
		this._current = this._current.next;
		return node;
	}

	//---------------------------------
	//  prev
	//---------------------------------

	public function get prev():* {
		var node:	DoubleLinkedListNode = this._current;
		this._current = this._current.prev;
		return node;
	}

	//---------------------------------
	//  current
	//---------------------------------

	public function get current():* {
		return this._current;
	}

}